#include "basecurve.h"

BaseCurve::BaseCurve(){
}

BaseCurve::BaseCurve(Points points)
{
    /*Is assumed that points is sorted correctly in counter clockwise orientation. The i'th and (i+1)th points forms a edge in the base curve.*/
    this->points = points;
}

BaseCurve::BaseCurve(Points points , Vectors normals){
    this->points = points;
    this->normals = normals;
}

void BaseCurve::drawNormals(){
    //Draws segments that represent the normals for each point in points. A normal is drawned from point(i) to normal(i)
    Point_3 point;
    Vector_3 normal;

    glColor3f(1.0 , 1.0 , 1.0);

    for(int i = 0 ; i < points.size() ; i++){
        point = points.at(i);
        normal = (20.0 * normals.at(i)) + Vector_3(point.x() , point.y() , point.z());

        glBegin(GL_LINES);
            glVertex3d(point.x() , point.y() , point.z());
            glVertex3d(normal.x() , normal.y() , normal.z());
        glEnd();
    }
}

Vectors BaseCurve::calculateNormals(double glPrevious[16] , double glPresent[16] , double glNext[16]){
    /*Calculates the normals of the base curve transformed by glPresent transformation. The normal for one vertice is the average
        between the normals of the planes where that point is incident in the polyhedra.
    */

    Vectors normals;

    Transform_3 presentTransform(GL2CGALMatrix(glPresent));
    Transform_3 previousTransform(GL2CGALMatrix(glPrevious));
    Transform_3 nextTransform(GL2CGALMatrix(glNext));
    Points  pointsByPrevious , pointsByPresent , pointsByNext;
    Points::iterator it;

    //Transforming the points
    for(it = points.begin() ; it != points.end() ; it++){
        pointsByPrevious.push_back(previousTransform.transform(*it));
        pointsByPresent.push_back(presentTransform.transform(*it));
        pointsByNext.push_back(nextTransform.transform(*it));
    }

    int size = points.size();
    for(int i = 0 ; i < size ; i++){

        //Next and previous indices in the base curve.
        int nextInBase = (i + 1) % size;
        int previousInBase;
        if(i == 0)
            previousInBase = size - 1;
        else
            previousInBase = i - 1;

        Point_3 pByPrevious(pointsByPrevious.at(i));
        Point_3 pByPresent(pointsByPresent.at(i));
        Point_3 pByPresentPrevious(pointsByPresent.at(previousInBase));
        Point_3 pByPresentNext(pointsByPresent.at(nextInBase));
        Point_3 pByNext(pointsByNext.at(i));

        /*The points sorted by the order of the normalization operation. Each line is formed by the operands of one
        cross product except for its center which always be the same points.*/
        Point_3 sortedPoints[4][2];

        sortedPoints[0][0] = pByPresentPrevious;    sortedPoints[0][1] = pByNext;
        sortedPoints[1][0] = pByNext;               sortedPoints[1][1] = pByPresentNext;
        sortedPoints[2][0] = pByPresentNext;        sortedPoints[2][1] = pByPrevious;
        sortedPoints[3][0] = pByPrevious;           sortedPoints[3][1] = pByPresentPrevious;

        Vector_3 normal(CGAL::NULL_VECTOR);
        Vector_3 avgNormal(CGAL::NULL_VECTOR);

        for(int j = 0 ; j < 4 ; j++){
            /*
            printf("x1 = %lf y1 = %lf z1 = %lf x2 = %lf y2 = %lf z2 = %lf x3 = %lf y3 = %lf z3 = %lf\n" ,
                pByPresent.x() , pByPresent.y() , pByPresent.z() ,
                sortedPoints[j][0].x() , sortedPoints[j][0].y() , sortedPoints[j][0].z() ,
                sortedPoints[j][1].x() , sortedPoints[j][1].y() , sortedPoints[j][1].z());*/

            if(!CGAL::collinear(pByPresent , sortedPoints[j][0] , sortedPoints[j][1])){
                normal = CGAL::normal(pByPresent , sortedPoints[j][0] , sortedPoints[j][1]);
                //printf("normal%d %d = (%lf , %lf , %lf)\n" , i , j, normal.x() , normal.y() , normal.z());
                avgNormal = avgNormal + normal;
            }
            else{
                printf("collinear!\n");
            }
        }

        avgNormal = avgNormal / 4;

        //normalizing
        double modulo = sqrt(avgNormal * avgNormal);
        avgNormal = avgNormal / modulo;

        normals.push_back(avgNormal);

        //printf("avgNormal%d = (%lf , %lf , %lf) modulo = %lf\n\n" , i , avgNormal.x() , avgNormal.y() , avgNormal.z() , modulo);
    }
    return normals;
}

Vectors BaseCurve::calculateNormals(double glTransform1[16] , double glTransform2[16]){
    /*Calculates the normals in the BEGIN_OF_POLY and END_OF_POLY special cases. In these cases there are only 2 transformations
    envolved.
    If the special case is BEGIN_OF_POLY, then glTransform1 represents the first transformation of the base curve of this poly and
    glTransform2 represents the next transformation.
    If the special case is END_OF_POLY, then  glTransform1 represents the last transformation of the base curve of this poly and
    glTransform2 represents the previous tranformation.
    The normal for one vertice is the average between the normals of the planes where that point is incident in the polyhedra.
    */

    Vectors normals;

    Transform_3 transform1(GL2CGALMatrix(glTransform1));
    Transform_3 transform2(GL2CGALMatrix(glTransform2));
    Points  pointsByPresent , pointsByNext;
    Points::iterator it;

    for(it = points.begin() ; it != points.end() ; it++){
        pointsByPresent.push_back(transform1.transform(*it));
        pointsByNext.push_back(transform2.transform(*it));
    }

    int size = points.size();
    for(int i = 0 ; i < size ; i++){

        //Next and previous indices in the base curve.
        int nextInBase = (i + 1) % size;
        int previousInBase;
        if(i == 0)
            previousInBase = size - 1;
        else
            previousInBase = i - 1;

        Point_3 pByPresent(pointsByPresent.at(i));
        Point_3 pByNext(pointsByNext.at(i));

        /*The points sorted by the order of the normalization operation. Each line is formed by the operands of one
        cross product except for its center which always be the same point.*/
        Point_3 sortedPoints[2][2];

        Vector_3 normal(CGAL::NULL_VECTOR);
        Vector_3 avgNormal(CGAL::NULL_VECTOR);

        Point_3 pByPresentPrevious(pointsByPresent.at(previousInBase));
        Point_3 pByPresentNext(pointsByPresent.at(nextInBase));

        sortedPoints[0][0] = pByPresentPrevious;    sortedPoints[0][1] = pByNext;
        sortedPoints[1][0] = pByNext;               sortedPoints[1][1] = pByPresentNext;

        for(int j = 0 ; j < 2 ; j++){
            /*
            printf("x1 = %lf y1 = %lf z1 = %lf x2 = %lf y2 = %lf z2 = %lf x3 = %lf y3 = %lf z3 = %lf\n" ,
                pByPresent.x() , pByPresent.y() , pByPresent.z() ,
                sortedPoints[j][0].x() , sortedPoints[j][0].y() , sortedPoints[j][0].z() ,
                sortedPoints[j][1].x() , sortedPoints[j][1].y() , sortedPoints[j][1].z());*/

            if(!CGAL::collinear(pByPresent , sortedPoints[j][0] , sortedPoints[j][1])){
                normal = CGAL::normal(pByPresent , sortedPoints[j][0] , sortedPoints[j][1]);
                //printf("normal%d %d = (%lf , %lf , %lf)\n" , i , j, normal.x() , normal.y() , normal.z());
                avgNormal = avgNormal + normal;
            }
            else{
                printf("collinear!\n");
            }
        }

        avgNormal = avgNormal / 2;

        //normalizing
        double modulo = sqrt(avgNormal * avgNormal);
        avgNormal = avgNormal / modulo;

        normals.push_back(avgNormal);

        //printf("avgNormal%d = (%lf , %lf , %lf) modulo = %lf\n\n" , i , avgNormal.x() , avgNormal.y() , avgNormal.z() , modulo);
    }
    return normals;
}
